package ip2region

import (
	"net"
	"strings"

	"github.com/lionsoul2014/ip2region/binding/golang/xdb"
)

const (
	IP2REGION_REGION_FIELD_COUNT = 5
	IP2REGION_REGION_SEP_FLAG    = "|"
)

type Ip2Region struct {
	searcher *xdb.Searcher
}

// 国家|区域|省份|城市|ISP
type IpRegionInfo struct {
	Country  string
	Region   string
	Province string
	City     string
	ISP      string
}

func convertStr(str string) string {
	if len(str) == 0 {
		return ""
	}

	if 0 == strings.Compare(str, "0") {
		return ""
	}

	return str
}

func hasLocalIpAddr(ipAddr string) bool {
	ip := net.ParseIP(ipAddr)
	if ip.IsLoopback() {
		return true
	}

	ip4 := ip.To4()
	if ip4 == nil {
		return false
	}

	return ip4[0] == 10 || // 10.0.0.0/8
		(ip4[0] == 172 && ip4[1] >= 16 && ip4[1] <= 31) || // 172.16.0.0/12
		(ip4[0] == 169 && ip4[1] == 254) || // 169.254.0.0/16
		(ip4[0] == 192 && ip4[1] == 168) // 192.168.0.0/16

}

func NewWithVectorIndex(dbPath string) *Ip2Region {
	vIndex, err := xdb.LoadVectorIndexFromFile(dbPath)
	if err != nil || vIndex == nil {
		return nil
	}

	searcher, err := xdb.NewWithVectorIndex(dbPath, vIndex)
	if err != nil || searcher == nil {
		return nil
	}

	return &Ip2Region{
		searcher: searcher,
	}
}

func NewWithContentBuffer(dbPath string) *Ip2Region {
	buff, err := xdb.LoadContentFromFile(dbPath)
	if err != nil || buff == nil {
		return nil
	}

	searcher, err := xdb.NewWithBuffer(buff)
	if err != nil || searcher == nil {
		return nil
	}

	return &Ip2Region{
		searcher: searcher,
	}
}

func (ip *Ip2Region) GetRegion(ipAddr string) *IpRegionInfo {
	if hasLocalIpAddr(ipAddr) {
		return &IpRegionInfo{
			Country:  "内网地址(LocalIP)",
			Region:   "",
			Province: "",
			City:     "",
			ISP:      "",
		}
	}

	ipInfo, err := ip.searcher.SearchByStr(ipAddr)
	if err != nil {
		return nil
	}

	strs := strings.Split(ipInfo, IP2REGION_REGION_SEP_FLAG)
	if len(strs) != IP2REGION_REGION_FIELD_COUNT {
		return nil
	}

	return &IpRegionInfo{
		Country:  convertStr(strs[0]),
		Region:   convertStr(strs[1]),
		Province: convertStr(strs[2]),
		City:     convertStr(strs[3]),
		ISP:      convertStr(strs[4]),
	}
}

func (ip *Ip2Region) GetRegionString(ipAddr string) string {
	var info string
	ipRegionInfo := ip.GetRegion(ipAddr)
	if ipRegionInfo == nil {
		return ""
	}

	info = ""
	if len(ipRegionInfo.Country) > 0 {
		info = ipRegionInfo.Country
	}

	if len(ipRegionInfo.Province) > 0 {
		info = info + ipRegionInfo.Province
	}

	if len(ipRegionInfo.City) > 0 {
		info = info + ipRegionInfo.City
	}

	if len(ipRegionInfo.ISP) > 0 {
		info = info + " " + ipRegionInfo.ISP
	}

	return info
}
